package com.swak.nio_udp.multicast;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.StandardSocketOptions;
import java.net.UnknownHostException;

import com.swak.utils.Nets;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * 使用组播来通讯：这段代码在本机上无法通过，但是在台式机上可以通，感觉有安装了虚拟机有关
 * 
 * @author 618lf
 */
@Getter
@Setter
@Accessors(chain = true)
public class Peer {

	private MulticastSocket socket = null;
	private int port;
	private Thread loop;
	private String name;

	// @com.swak.utils.Nets
	private String MULTICAST_ADDRESS = "239.0.0.2";

	/**
	 * 组播如果不通，可以查看组播加入的地址设置到了哪张网卡上，可以在加入网络时指定设置的网卡。 出现不通的问题，一版有多张网卡！
	 * 
	 * @param name
	 * @param port
	 * @throws UnknownHostException
	 * @throws IOException
	 */
	public Peer(String name, int port) throws UnknownHostException, IOException {
		this.name = name;
		this.socket = new MulticastSocket(port);
		this.socket.joinGroup(new InetSocketAddress(InetAddress.getByName(MULTICAST_ADDRESS), port),
				Nets.getLocalNetwork());

		/*
		 * 一种不好的方式
		 */
//		this.socket = new MulticastSocket(port);
//		this.socket.joinGroup(InetAddress.getByName(MULTICAST_ADDRESS));

		/*
		 * 为true，表示禁用 IP_MULTICAST_LOOP
		 * 为false， 测试不通过
		 * 表示是否能接收自身的信息；如果单机的测试则需要开启此参数
		 */
		this.socket.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true);
		this.loop = new Thread(this::receive);
		this.loop.setDaemon(false);
		this.loop.start();
	}

	private void receive() {
		while (true) {
			try {
				byte[] recvBuffer = new byte[1024];
				final DatagramPacket recvPacket = new DatagramPacket(recvBuffer, recvBuffer.length);
				this.socket.receive(recvPacket);
				System.out.println("I am " + this.name);
				System.out.println("received packet from " + recvPacket.getAddress().getHostAddress() + " : "
						+ recvPacket.getPort());
				// 注意由于DatagramPacket的缓冲区复用，本次收到的最后一个字符后并不会补'\0',而是使用一个长度标记
				String msg = new String(recvPacket.getData(), recvPacket.getOffset(), recvPacket.getLength());
				System.out.println("received " + msg);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public void send(int port, String message) throws IOException {
		byte[] buffer = message.getBytes();
		DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
		packet.setAddress(InetAddress.getByName(MULTICAST_ADDRESS));
		packet.setPort(port);
		socket.send(packet);
	}
}